package com.module.common.base.service.impl;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.lianxin.common.bean.entity.MpBaseEntity;
import com.lianxin.common.mybatis.plus.mapper.MpBaseMapper;
import com.lianxin.common.mybatis.plus.service.MpCrudService;
import com.lianxin.common.mybatis.plus.util.UpdateUtil;
import com.lianxin.common.user.context.UserInfoContext;
import com.lianxin.common.user.context.UserInfoContextHolder;

public class MpCrudServiceImpl<T extends MpBaseEntity, R extends MpBaseMapper<T>>
		implements MpCrudService<T> {

	private static final Logger logger = LoggerFactory
			.getLogger(MpCrudServiceImpl.class);

	private static final int ARCHIVE_STATE = 0;

	protected R repository;

	private final Class<T> entityClass;

	public MpCrudServiceImpl(R repository) {
		this.repository = repository;

		// 获取范型子类的类型
		Type genType = getClass().getGenericSuperclass();
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

		entityClass = (Class) params[0];
	}

	@Override
	public Optional<T> findOne(Long id) {
		QueryWrapper<T> queryWrapper = new QueryWrapper<>();
		queryWrapper.ne("state", ARCHIVE_STATE).eq("id", id);
		return Optional.ofNullable(repository.selectOne(queryWrapper));
	}

	@Override
	public List<T> findAll() {
		QueryWrapper<T> queryWrapper = new QueryWrapper<>();
		queryWrapper.ne("state", ARCHIVE_STATE);
		return repository.selectList(queryWrapper);
	}

	@Override
	public List<T> findByIds(Collection<Long> ids) {
		return this.findByIds(ids, true);
	}

	@Override
	public Optional<T> findAllState(Long id) {
		T t = repository.selectById(id);
		return Optional.ofNullable(t);
	}

	@Override
	public List<T> findAllState() {
		return repository.selectList(null);
	}

	@Override
	public List<T> findAllState(Collection<Long> ids) {
		return this.findByIds(ids, false);
	}

	/**
	 * 通过ids查询
	 *
	 * @param ids
	 *            ids
	 * @param queryUnArchive
	 *            是否查询未归档信息
	 */
	private List<T> findByIds(Collection<Long> ids, Boolean queryUnArchive) {
		if (CollectionUtils.isEmpty(ids)) {
			return Collections.emptyList();
		}

		QueryWrapper<T> queryWrapper = new QueryWrapper<>();
		queryWrapper.in("id", ids);
		if (queryUnArchive) {
			queryWrapper.ne("state", ARCHIVE_STATE);
		}
		return repository.selectList(queryWrapper);
	}

	@Override
	public T create(T resource) {
		this.assembleBaseInfo(resource, true);
		repository.insert(resource);
		return resource;
	}

	/**
	 * 装配基础信息 CreatedBy UpdatedBy
	 *
	 * @param resource
	 *            需要装配信息的实体
	 * @param isCreate
	 *            是否是新增
	 */
	private void assembleBaseInfo(T resource, boolean isCreate) {
		UserInfoContext context = UserInfoContextHolder.getContext();
		if (Objects.nonNull(context)) {
			Long userId = context.getUserId();
			if (isCreate) {
				resource.setCreatedBy(userId);
			}
			resource.setUpdatedBy(userId);
		}
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public List<T> batchCreate(List<T> resources) {
		return resources.stream().peek(resource -> {
			this.assembleBaseInfo(resource, true);
			repository.insert(resource);
		}).collect(Collectors.toList());
	}

	@Override
	public void update(T resource) {
		T update = repository.selectById(resource.getId());

		if (Objects.isNull(update)) {
			throw new IllegalArgumentException("invalid "
					+ entityClass.getName() + " id:" + resource.getId());
		}

		UpdateUtil.copyNonNullProperties(resource, update);

		this.assembleBaseInfo(resource, false);
		repository.updateById(update);
	}

	@Override
	public void updateByState(Long id, Integer state, T resource) {
		T update = repository.selectById(id);
		if (Objects.isNull(update)) {
			throw new IllegalArgumentException(
					"invalid " + entityClass.getName() + " id:" + id);
		}
		UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
		updateWrapper.eq("id", id);
		update.setState(state);

		this.assembleBaseInfo(update, false);
		repository.update(update, updateWrapper);
	}

	@Override
	public void updateAllProperties(T resource) {
		this.assembleBaseInfo(resource, false);
		repository.updateById(resource);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void batchUpdate(List<T> resources) {
		resources.forEach(resource -> repository.updateById(resource));
	}

	@Override
	public void delete(Long id) {
		repository.deleteById(id);
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public void batchDelete(List<Long> ids) {
		ids.forEach(id -> repository.deleteById(id));
	}

	@Override
	public void achieve(Long id) {
		T update = repository.selectById(id);

		if (Objects.isNull(update)) {
			return;
		}
		update.setState(ARCHIVE_STATE);

		this.assembleBaseInfo(update, false);
		repository.updateById(update);
	}

	@Override
	public Map<Long, T> buildIdMap(List<T> entities) {
		return entities.stream()
				.collect(Collectors.toMap(MpBaseEntity::getId, t -> t));
	}

	@Override
	public Map<Long, T> getIdMap(List<Long> ids) {
		List<T> entities = this.findByIds(ids);
		return this.buildIdMap(entities);
	}
}
